<?php
final class XRouter extends XObject{
	private static $instance = null;
	private $dir = '';
	private $extensions = array();
	private $hasExtensions = false;
	private $view = null;
	
	private function __construct() {
	
	}
	
	/**
	 * @return XRouter
	 */	
	static function singleton() {
		if (!self::$instance) {
			self::$instance = new self;
		}
		return self::$instance;
	}
	
	public function start(){		
		$this->_init();
		$uri = $_SERVER["REQUEST_URI"];
		preg_match("/^([^\?]+)/i", $uri, $match);
		if(!isset($match[1])){
			throw new XRouteNotFoundException("url {$uri} not valid");
			return;
		}
		$route = $match[1];
		$params = $this->_params($uri);	
		$this->forward($route, $params);	
	}
	
	private function _init(){
		$this->dir = PROJECT_ROOT.'/controllers';
		$this->extensions = XConfig::singleton()->get('project.route.extensions');
		$this->hasExtensions = is_array($this->extensions) && count($this->extensions) > 0;		
		$this->view = XConfig::singleton()->get('view.default.class');		
		if(!$this->view
			|| !class_exists($this->view, false)
			|| !XClassLoader::loader($this->view)->implementsInterface('XView')){
			$this->view = 'XSmartyView';
		}
	}

	private function _params($uri){
		$params = array();
		preg_match("/\?([^\?]+)$/i", $uri, $match);
		if(!isset($match[1])){		
			return $params;
		}
		$array = preg_split("/&+/i", $match[1]);
		foreach($array as $value){
			$pair = preg_split("/=+/i", $value);
			if(isset($pair[0])){
				$params[urldecode($pair[0])] = isset($pair[1]) ? urldecode($pair[1]) : null;
			}
		}
		return $params;
	}
	
	public function loadController($route, $file, $className, $methodName, $params = array()){
		$newFile = preg_replace("/[\\|\/]+/i", DIRECTORY_SEPARATOR, $file);
		$newRoute = preg_replace("/[\\|\/]+/i", '/', $route);
		if(!file_exists($newFile)){
			return false;
		}
		$runned = false;
		if(!class_exists($className, false)){
			x_require($newFile);
		}else{
			//check controller class name clash
			$this->_checkControllerClash($newRoute, $newFile, $className, $params);
		}
		$loader = XClassLoader::loader($className);
		if($loader->isSubclassOf('XController')
			&& $loader->hasMethod($methodName)){
			$controller = XFactory::singleton()->get($className);
			$controller->setRoute($newRoute);
			$controller->setAction($methodName);		
			$controller->addParams($params);
			$controller->setView(XFactory::singleton()->get($this->view));
			$controller->exec();
			$runned = true;
		}
		return $runned;
	}
	
	private function _checkControllerClash($route, $file, $className, $params){				
		$oldFile = preg_replace("/[\\|\/]+/i", DIRECTORY_SEPARATOR, XClassLoader::loader($className)->reflection()->getFileName());
		if($oldFile != $file){
			if(empty($params)){
				XServletResponse::singleton()->redirect($route);
			}
			$url_params = array();
			foreach($params as $key => $value){
				if(is_array($value) || is_object($value)){
					continue;
				}
				$url_params[] ="{$key}={$value}";
			}
			$location = $route.'?'.implode('&', $url_params);
			XServletResponse::singleton()->redirect($location);
		}
	}
	
	public function forward($route, $params = array()){	
		if($this->hasExtensions){
			foreach($this->extensions as $key => $value){
				if(!preg_match("/{$key}/i", $route)){
					continue;
				}
				if(!class_exists($value, false)){
					continue;
				}
				if(!XClassLoader::loader($value)->implementsInterface(XRouteParser)){
					continue;
				}
				$parser = XFactory::singleton()->get($value);	
				if($parser->parse($this->dir, $route, $params)){					
					return true;
				}
			}
		}
		$parser = XFactory::singleton()->get('XRESTRouteParser');
		if($parser->parse($this->dir, $route, $params)){
			return true;
		}
		throw new XRouteNotFoundException("route not found in {$route}");	
	}
}
?>